#ifndef SIMMODEL_H
#define SIMMODEL_H

#include <QtGui/QMainWindow>
#include "ui_simmodel.h"
#include <QtGui/QWidget>
#include <QtGui/QSpacerItem>

#include <vector>
#include <list>
#include <utility>
#include <memory>
#include <QVector>
#include <qwt_plot_canvas.h>
#include <qwt_legend.h>
#include <qwt_plot_grid.h>
#include <qwt_plot_curve.h>
#include <qwt_symbol.h>
#include <qwt_scale_widget.h>
#include <Hydraulics.h>
#include <oilpipe.h>
#include <numerical_pde.h>
#include <QComboBox>
#include <QSpinBox>
#include <QLabel>
#include <qwthelper.h>
#include <QMessageBox>

using namespace hydraulics;
using namespace pdelib;
using namespace pdelib::char_method;
using namespace pdelib::force_flow;
using namespace pdelib::force_nonisotherm;
using namespace std;

class simModel : public QMainWindow
{
	Q_OBJECT

public:
	simModel(QWidget *parent = 0, Qt::WFlags flags = 0);
	~simModel();

	const pair<vector<string>, vector<string>> getSetOfProperties(string parameters){
		vector<string> property, defaultUnit;
		vector<bool> allowToSetScript;
		property.push_back("PT_INLET_Value");							defaultUnit.push_back("Pa");		allowToSetScript.push_back(true);
		property.push_back("PT_OUTLET_Value");							defaultUnit.push_back("Pa");		allowToSetScript.push_back(true);
		//property.push_back("QT_Value");								defaultUnit.push_back("m3/h");		allowToSetScript.push_back(true);
		property.push_back("PROFILE");									defaultUnit.push_back("-");			allowToSetScript.push_back(false);

		property.push_back("DENSITY");									defaultUnit.push_back("kg/m3");		allowToSetScript.push_back(false);
		property.push_back("DINAMIC_VISCOSITY");						defaultUnit.push_back("CP");		allowToSetScript.push_back(false);
		property.push_back("ABS_ROUGHNESS");							defaultUnit.push_back("m");			allowToSetScript.push_back(false);
		property.push_back("DIAMETER");									defaultUnit.push_back("m");			allowToSetScript.push_back(false);
		property.push_back("MAX_SEGMENT_LENGTH");						defaultUnit.push_back("m");			allowToSetScript.push_back(false);
		property.push_back("DT");										defaultUnit.push_back("s");			allowToSetScript.push_back(false);
		property.push_back("SIM_SPEED");								defaultUnit.push_back("s");			allowToSetScript.push_back(false);	
		property.push_back("END_TIME");									defaultUnit.push_back("s");			allowToSetScript.push_back(false);

		property.push_back("PT_INLET_Noise");							defaultUnit.push_back("Pa");		allowToSetScript.push_back(true);
		property.push_back("PT_INLET_Interference");					defaultUnit.push_back("Pa");		allowToSetScript.push_back(true);
		property.push_back("PT_INLET_Interference_Rnd_Ampl_E");			defaultUnit.push_back("Pa");		allowToSetScript.push_back(true);
		property.push_back("PT_INLET_Interference_Rnd_Ampl_SKO");		defaultUnit.push_back("Pa");		allowToSetScript.push_back(true);
		property.push_back("PT_INLET_Interference_Rnd_Duration_E");		defaultUnit.push_back("s");			allowToSetScript.push_back(true);
		property.push_back("PT_INLET_Interference_Rnd_Duration_SKO");	defaultUnit.push_back("s");			allowToSetScript.push_back(true);

		property.push_back("PT_OUTLET_Noise");							defaultUnit.push_back("Pa");		allowToSetScript.push_back(true);
		property.push_back("PT_OUTLET_Interference");					defaultUnit.push_back("Pa");		allowToSetScript.push_back(true);
		property.push_back("PT_OUTLET_Interference_Rnd_Ampl_E");		defaultUnit.push_back("Pa");		allowToSetScript.push_back(true);
		property.push_back("PT_OUTLET_Interference_Rnd_Ampl_SKO");		defaultUnit.push_back("Pa");		allowToSetScript.push_back(true);
		property.push_back("PT_OUTLET_Interference_Rnd_Duration_E");	defaultUnit.push_back("s");			allowToSetScript.push_back(true);
		property.push_back("PT_OUTLET_Interference_Rnd_Duration_SKO");	defaultUnit.push_back("s");			allowToSetScript.push_back(true);

		property.push_back("QT_Noise");									defaultUnit.push_back("m3/h");		allowToSetScript.push_back(true);
		property.push_back("QT_Interference");							defaultUnit.push_back("m3/h");		allowToSetScript.push_back(true);
		property.push_back("QT_Interference_Rnd_Ampl_E");				defaultUnit.push_back("m3/h");		allowToSetScript.push_back(true);
		property.push_back("QT_Interference_Rnd_Ampl_SKO");				defaultUnit.push_back("m3/h");		allowToSetScript.push_back(true);
		property.push_back("QT_Interference_Rnd_Duration_E");			defaultUnit.push_back("s");			allowToSetScript.push_back(true);
		property.push_back("QT_Interference_Rnd_Duration_SKO");			defaultUnit.push_back("s");			allowToSetScript.push_back(true);
		//QT_Interferencedelay

		if (parameters == "all"){
			return make_pair(property, defaultUnit);
		}else{
			vector<string> propertyWSetScript, defaultUnitWSetScript;
			for (unsigned i = 0; i < allowToSetScript.size(); i++)
				if (allowToSetScript[i] == true){
					propertyWSetScript.push_back(property[i]);
					defaultUnitWSetScript.push_back(defaultUnit[i]);
				}
				return make_pair(propertyWSetScript, defaultUnitWSetScript);
		}
	}
	const vector<vector<string>> getSetOfCalcProperties(){
		vector<string> property;
		vector<vector<string>> properties;
		property.push_back("PRESSURE");			property.push_back("Pa");		property.push_back("both");
		properties.push_back(property);	property.clear();
		property.push_back("FLOW");				property.push_back("m3/h");		property.push_back("both");
		properties.push_back(property);	property.clear();
		property.push_back("PIPE PROFILE");		property.push_back("m");		property.push_back("profile");
		properties.push_back(property);
		return properties;
	}
	const pair<vector<vector<string>>, vector<vector<double>>> getVectorUnitAndCoeff(){
		vector<vector<string>> unit;	vector<string> u;
		vector<vector<double>> coeff;	vector<double> c;
		//pressure;
		u.push_back("Pa");	 c.push_back(1);
		u.push_back("kPa");	 c.push_back(1e3);
		u.push_back("MPa");	 c.push_back(1e6);
		u.push_back("atm");	 c.push_back(101325);
		u.push_back("bar");	 c.push_back(1e5);
		u.push_back("psi");  c.push_back(6894.76);
		unit.push_back(u);   coeff.push_back(c);
		//flow
		u.clear(); c.clear(); 
		u.push_back("m3/h");    c.push_back(1);
		u.push_back("m3/s");   c.push_back(2.777777777777778e-4);
		u.push_back("m3/m");   c.push_back(0.0166666666666667);
		u.push_back("m3/d");   c.push_back(60);
		unit.push_back(u);   coeff.push_back(c);
		//distance
		u.clear(); c.clear(); 
		u.push_back("m");    c.push_back(1);
		u.push_back("cm");   c.push_back(1e-2);
		u.push_back("mm");   c.push_back(1e-3);
		u.push_back("km");   c.push_back(1e3);
		u.push_back("ft");   c.push_back(0.305);
		unit.push_back(u);   coeff.push_back(c);
		//time
		u.clear(); c.clear(); 
		u.push_back("s");    c.push_back(1);
		u.push_back("M");    c.push_back(60);
		u.push_back("h");    c.push_back(3600);
		u.push_back("d");    c.push_back(86400);
		unit.push_back(u);   coeff.push_back(c);
		//density
		u.clear(); c.clear(); 
		u.push_back("kg/m3"); c.push_back(1);
		u.push_back("g/cm3"); c.push_back(1e3);
		unit.push_back(u);    coeff.push_back(c);
		//viscosity
		u.clear(); c.clear(); 
		u.push_back("CP");     c.push_back(1);
		u.push_back("P");      c.push_back(1e2);
		u.push_back("kg/m-h"); c.push_back(0.277777777778);
		u.push_back("kg/m-s"); c.push_back(1e3);
		u.push_back("Pa-s");   c.push_back(1e3);
		unit.push_back(u);     coeff.push_back(c);
		//-
		u.clear(); c.clear(); 
		u.push_back("-");   c.push_back(1);
		unit.push_back(u);     coeff.push_back(c);
		//...
		u.clear(); c.clear(); 
		u.push_back("...");   c.push_back(1);
		unit.push_back(u);     coeff.push_back(c);
		//
		return make_pair(unit, coeff);
	}
	const double getCoeff(string u){
		pair<vector<vector<string>>, vector<vector<double>>> p = getVectorUnitAndCoeff();
		for (unsigned int i = 0; i < p.first.size(); ++i)
			if(std::find(p.first[i].begin(), p.first[i].end(), u) != p.first[i].end())
				for (unsigned int j = 0; j < p.first[i].size(); ++j)
					if (p.first[i][j] == u) return p.second[i][j];
		QMessageBox::critical(this, "Error", "Unit not found.", QMessageBox::Ok);
	}
	const vector<string> getSetOfUnits(string u){
		pair<vector<vector<string>>, vector<vector<double>>> p = getVectorUnitAndCoeff();
		for (unsigned int i = 0; i < p.first.size(); ++i)
			if(std::find(p.first[i].begin(), p.first[i].end(), u) != p.first[i].end()) return p.first[i];
		QMessageBox::critical(this, "Error", "Unit not found.", QMessageBox::Ok);
	}

	struct scriptElement{
		double time;
		double value;
		string unit;
	};

private:
    struct {
        auto_ptr<hyperbolic_pde> pde;
        auto_ptr<base_pde_solver> solver;
        auto_ptr<method_of_characterstics_data> buffer;
        pipeline_profile_t pipeprofile;
        auto_ptr<boundary_condition_t> left_boundary, right_boundary;
    } model_data;

	map<string, vector<vector<double>>> calc_data;
    list <map<wstring, QVector<double>>> data;
    vector<string> data_keywords;

private:
	Ui::simModelClass ui;
	vector<int> currentIndexComboBox;
	void openProperties();
	void saveProperties();
	void showProperties(bool openExistFile = false);
	void showVarSelection(string type);
	double getProperty(string propertyName);
	bool setPropertyValue(string propertyName, double propertyValue);
	map<string, vector<scriptElement>> script;
	bool addGraphTab(string typePlot, vector<string> Name,  vector<string> Unit);

	QString filePath;
	void log(string text);
private slots:
	void on_bTest_clicked();
	void on_bTest2_clicked();
    void on_bStart_clicked();    
	void on_bLog_clicked();
	void on_bAddTrendPlot_clicked();
	void on_bAddProfilePlot_clicked();
	void on_bScriptEditor_clicked();
	void on_mNew_activated();
	void on_mProperties_activated();
	void on_mSave_activated();
	void on_mSaveAs_activated();
	void on_mOpen_activated();
	void comboBoxIndexChanged(const int &row);
	void slaveFormShow(const int &row);
    void add_layer();
	void showTab(QWidget *wid);
signals:
	void setDataInThread(int, double);
};


class ModelWorker : public QObject {     
    Q_OBJECT
private:
    base_pde_solver *solver;
    boundary_condition_t *left_boundary, *right_boundary;
    bool finishing;
	int end_time;
	double dt;
public:
    ModelWorker(base_pde_solver *solver, boundary_condition_t* _left_boundary, 
        boundary_condition_t* _right_boundary);
    ~ModelWorker();
public slots:
    void process();
	void getData(int _end_time, double _dt);
signals:
    void finished();
    void layer_calculated();
};

class itemTab : public QObject {     
	Q_OBJECT
private:
	QWidget *wid;
	QVBoxLayout *vLayout;
	QwtPlot *plot;
	QwtLegend *leg;
	QwtPlotGrid *grid;
	QSlider *slider;
	vector<QwtPlotCurve *> curve;
	QHBoxLayout *hSliderLayout;
	QLabel * lblCurrentTime;
	QSpacerItem *hSliderSpaser1;
	QSpacerItem *hSliderSpaser2;
	QHBoxLayout *hLayout;
	QPushButton *btnTrackValue;
	QPushButton *btnRefresh;
	QLabel * lblOutput;
	QComboBox *cbSelOutput;
	QSpacerItem *hSpaser;
	QLabel * lblTime;
	QSpinBox *sbScrollingSpeed;
	QComboBox *cbSelTimeUnit;
	QPushButton *btnPlay;
	QPushButton *btnStop;
	vector<QColor> color;

	struct data_to_display_graph{
		string type;
		vector<string> name;
		vector<string> unit;
		vector<vector<QVector<double>>> value;
		QVector<double> time; 
		QVector<double> coordinates;
	};
	data_to_display_graph data_to_display;
public:
	itemTab();
	~itemTab();

public slots:
	void refreshPlot(int value);
	void addTab();
	void addPlot(string type, vector<string> name, vector<string> unit, vector<vector<vector<double>>> value, vector<double> time, vector<double> profile);
signals:
	void tabAdded(QWidget *wid);
};


#endif // SIMMODEL_H
